/*
 / _____)             _              | |
( (____  _____ ____ _| |_ _____  ____| |__
 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 _____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
  (C)2013 Semtech-Cycleo

Description:
    Configure LoRa concentrator and record received packets in a log file

License: Revised BSD License, see LICENSE.TXT file include in the project
*/


/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */

/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif

#include <stdint.h>     /* C99 types */
#include <stdbool.h>    /* bool type */
#include <stdio.h>      /* printf fprintf sprintf fopen fputs */

#include <string.h>     /* memset */
#include <signal.h>     /* sigaction */
#include <time.h>       /* time clock_gettime strftime gmtime clock_nanosleep*/
#include <unistd.h>     /* getopt access */
#include <stdlib.h>     /* atoi */

/* for directory create */
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

#include <pthread.h>

#include "parson.h"
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_aux.h"


/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */

#define ARRAY_SIZE(a)   (sizeof(a) / sizeof((a)[0]))
#define MSG(args...)    fprintf(stderr,"loragw_pkt_logger: " args) /* message that is destined to the user */

/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */

#define COM_PATH_DEFAULT "/dev/ttyS4"

#define LOG_PATH_DEFAULT "./log"

#define FETCH_SLEEP_MS      10
/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */

/* signal handling variables */
struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */

pthread_t pthread_tx;
pthread_t pthread_rx;

pthread_mutex_t pthrid_mtx = PTHREAD_MUTEX_INITIALIZER;

/* test module type */
uint8_t module_type = 0; /* 0:send and receive test,  1:send test, 2:receive test */
bool log_en = false; /* record log into file, 0:disable, 1:enable */

int log_rotate_interval = 3600; /* by default, rotation every hour */

/* configuration variables needed by the application  */
uint64_t lgwm = 0; /* LoRa gateway MAC address */
char lgwm_str[17];

/* clock and log file management */
time_t now_time;
time_t log_start_time;
FILE * log_file = NULL;
char log_file_name[64];
char log_path_name[64];
unsigned long pkt_in_log = 0; /* count the number of packet written in each log file */

char *m_str[5] = {"for both send and receive", "for send only", "for receive only", "for tx continus", "for tx continus and receive"};

/* application parameters */
char mod[64] = "LORA";
uint32_t f_target = 433100000; /* target frequency - invalid default value, has to be specified by user */
int rf_chain = 0;
int sf = 7; /* SF10 by default */
int cr = 1; /* CR1 aka 4/5 by default */
int bw = 125; /* 125kHz bandwidth by default */
int power = 14; /* 14 dBm by default */
int preamb = 8; /* 8 symbol preamble by default */
int pl_size = 32; /* 16 bytes payload by default */
int delay = 1000; /* 1 second between packets by default */
int repeat = -1; /* by default, repeat until stopped */
bool invert = false;
float br_kbps = 50;
uint8_t fdev_khz = 25;

uint8_t cmd_f = 0; // cmd freqency or not, 0:n, 1:y
uint8_t cmd_p = 0; // cmd rf power or not, 0:n, 1:y


static struct lgw_tx_gain_lut_s txlut; /* TX gain table */

#if 0
/* TX gain LUT table */
static struct lgw_tx_gain_lut_s txgain_lut = {
    .size           = 5,
    .lut[0] = {
        .dig_gain   = 0,
        .pa_gain    = 0,
        .dac_gain   = 3,
        .mix_gain   = 12,
        .rf_power   = 0
    },
    .lut[1] = {
        .dig_gain   = 0,
        .pa_gain    = 1,
        .dac_gain   = 3,
        .mix_gain   = 12,
        .rf_power   = 10
    },
    .lut[2] = {
        .dig_gain   = 0,
        .pa_gain    = 2,
        .dac_gain   = 3,
        .mix_gain   = 10,
        .rf_power   = 14
    },

    .lut[3] = {
        .dig_gain   = 0,
        .pa_gain    = 3,
        .dac_gain   = 3,
        .mix_gain   = 9,
        .rf_power   = 20
    },
    .lut[4] = {
        .dig_gain   = 0,
        .pa_gain    = 3,
        .dac_gain   = 3,
        .mix_gain   = 14,
        .rf_power   = 27
    }
};
#endif

/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */

static void sig_handler(int sigio);

int parse_tx_parameters(const char * conf_file);

int parse_SX1301_configuration(const char * conf_file);

int parse_gateway_configuration(const char * conf_file);

void open_log(void);

void record_log(FILE *log, struct lgw_pkt_rx_s *p, char *fetch_timestamp);

void usage (void);

void mcu_reset_control(void);

void tx_thread(void *arg);

void rx_thread(void *arg);


/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */

static void exit_cleanup(void) {
    int i;
    
    MSG("INFO: Stopping concentrator\n");
    i = lgw_stop();
    if (i == LGW_HAL_SUCCESS) {
        MSG("INFO: concentrator stopped successfully\n");
    } else {
        MSG("WARNING: failed to stop concentrator successfully\n");
    }
}

static void sig_handler(int sigio) {
    MSG("\nINFO: Ready to exit module test program\n");
    if (sigio == SIGQUIT) {
        quit_sig = 1;
    } else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
        exit_sig = 1;
    }
}


void tx_continus(struct lgw_pkt_tx_s *pkt, uint8_t *payload, uint8_t size)
{
    /* fill-up payload and parameters */
    pkt->freq_hz = f_target;
    pkt->tx_mode = IMMEDIATE;
    pkt->rf_chain = rf_chain;
    pkt->rf_power = power;
    if (strcmp(mod, "FSK") == 0) {
        pkt->modulation = MOD_FSK;
        pkt->datarate = br_kbps * 1e3;
        pkt->f_dev = fdev_khz;
    } else {
        pkt->modulation = MOD_LORA;
        switch (bw) {
            case 125: pkt->bandwidth = BW_125KHZ; break;
            case 250: pkt->bandwidth = BW_250KHZ; break;
            case 500: pkt->bandwidth = BW_500KHZ; break;
            default:
                MSG("ERROR: invalid 'bw' variable\n");
                return ;
        }
        switch (sf) {
            case  7: pkt->datarate = DR_LORA_SF7;  break;
            case  8: pkt->datarate = DR_LORA_SF8;  break;
            case  9: pkt->datarate = DR_LORA_SF9;  break;
            case 10: pkt->datarate = DR_LORA_SF10; break;
            case 11: pkt->datarate = DR_LORA_SF11; break;
            case 12: pkt->datarate = DR_LORA_SF12; break;
            default:
                MSG("ERROR: invalid 'sf' variable\n");
                return ;
        }
        switch (cr) {
            case 1: pkt->coderate = CR_LORA_4_5; break;
            case 2: pkt->coderate = CR_LORA_4_6; break;
            case 3: pkt->coderate = CR_LORA_4_7; break;
            case 4: pkt->coderate = CR_LORA_4_8; break;
            default:
                MSG("ERROR: invalid 'cr' variable\n");
                return;
        }
    }
    pkt->invert_pol = invert;
    pkt->preamble = preamb;
    pkt->size = size;

    pkt->preamble = 65535;
    pkt->invert_pol = false;
    pkt->no_crc = true;
    pkt->no_header = true;
    pkt->size = 1;
    pkt->payload[0] = 0;

    memcpy(pkt->payload, payload, pkt->size);  /* abc.. is for padding */
    lgw_reg_w(LGW_TX_MODE, 1); /* Tx continuous */
}

/* tx thread */
void tx_thread(void *arg)
{
    int i, j;
    uint8_t status_var;
    /* allocate memory for packet sending */
    struct lgw_pkt_tx_s txpkt; /* array containing 1 outbound packet + metadata */

    /* loop variables (also use as counters in the packet payload) */
    uint16_t cycle_count = 0;
    uint8_t payload[256] = { 0 };

    /* main loop */
    for(i = 2; i < 256; i++){
        payload[i] = i - 2;
    }

    if((arg != NULL) && ((*(uint8_t*)arg == 3) || (*(uint8_t*)arg == 4))){ 
        tx_continus(&txpkt, payload, pl_size);
        pthread_mutex_lock(&pthrid_mtx);
        /* send packet */
        MSG("INFO: Sending packet %s ...", m_str[3]);
        i = lgw_send(txpkt); /* non-blocking scheduling of TX packet */
        if (i == LGW_HAL_ERROR) {
            printf("ERROR\n");
            pthread_mutex_unlock(&pthrid_mtx);
            return ;
        }
        else {
            printf("OK\n");
            pthread_mutex_unlock(&pthrid_mtx);
            /* waiting for user input */
            while ((quit_sig != 1) && (exit_sig != 1)) {
                wait_ms(100);
            }
            return ;
        }
    }

    while ((quit_sig != 1) && (exit_sig != 1)) {
        /* fill-up payload and parameters */
        memset(&txpkt, 0, sizeof(txpkt));
        txpkt.freq_hz = f_target;
        txpkt.tx_mode = IMMEDIATE;
        txpkt.rf_chain = rf_chain;
        txpkt.rf_power = power;
        if (strcmp(mod, "FSK") == 0) {
            txpkt.modulation = MOD_FSK;
            txpkt.datarate = br_kbps * 1e3;
            txpkt.f_dev = fdev_khz;
        } else {
            txpkt.modulation = MOD_LORA;
            switch (bw) {
                case 125: txpkt.bandwidth = BW_125KHZ; break;
                case 250: txpkt.bandwidth = BW_250KHZ; break;
                case 500: txpkt.bandwidth = BW_500KHZ; break;
                default:
                    MSG("ERROR: invalid 'bw' variable\n");
                    return ;
            }
            switch (sf) {
                case  7: txpkt.datarate = DR_LORA_SF7;  break;
                case  8: txpkt.datarate = DR_LORA_SF8;  break;
                case  9: txpkt.datarate = DR_LORA_SF9;  break;
                case 10: txpkt.datarate = DR_LORA_SF10; break;
                case 11: txpkt.datarate = DR_LORA_SF11; break;
                case 12: txpkt.datarate = DR_LORA_SF12; break;
                default:
                    MSG("ERROR: invalid 'sf' variable\n");
                    return ;
            }
            switch (cr) {
                case 1: txpkt.coderate = CR_LORA_4_5; break;
                case 2: txpkt.coderate = CR_LORA_4_6; break;
                case 3: txpkt.coderate = CR_LORA_4_7; break;
                case 4: txpkt.coderate = CR_LORA_4_8; break;
                default:
                    MSG("ERROR: invalid 'cr' variable\n");
                    return;
            }
        }
        txpkt.invert_pol = invert;
        txpkt.preamble = preamb;
        txpkt.size = pl_size;
        memcpy(txpkt.payload, payload, pl_size);  /* abc.. is for padding */
        /* main loop */
        cycle_count = 0;
        while ((repeat == -1) || (cycle_count < repeat)) {
            ++cycle_count;
        
            /* refresh counters in payload (big endian, for readability) */
            txpkt.payload[0] = (uint8_t)(cycle_count >> 8); /* MSB */
            txpkt.payload[1] = (uint8_t)(cycle_count & 0x00FF); /* LSB */
        

            pthread_mutex_lock(&pthrid_mtx);
            /* send packet */
            MSG("INFO: Sending packet number %u  ", cycle_count);
            i = lgw_send(txpkt); /* non-blocking scheduling of TX packet */
            if (i == LGW_HAL_ERROR) {
                printf("ERROR\n");
                pthread_mutex_unlock(&pthrid_mtx);
                return ;
            }
            else {
                printf("OK\n");
                j = 0;
                /* wait for packet to finish sending */
                do {
                    j++;
                    wait_ms(5);
                    lgw_status(TX_STATUS, &status_var); /* get TX status */
                } while ((status_var != TX_FREE) && (j < 200));
            }
            pthread_mutex_unlock(&pthrid_mtx);
            /* wait inter-packet delay */
            wait_ms(delay);
        
            /* exit loop on user signals */
            if ((quit_sig == 1) || (exit_sig == 1)) {
                break;
            }
        }
        break;
    }
}

/* rx thread */
void rx_thread(void *arg)
{
    int i;
    /* clock and log rotation management */
    //int log_rotate_interval = 3600; /* by default, rotation every hour */
    int time_check = 0; /* variable used to limit the number of calls to time() function */

    /* allocate memory for packet fetching and processing */
    struct lgw_pkt_rx_s rxpkt[16]; /* array containing up to 16 inbound packets metadata */
    struct lgw_pkt_rx_s *p; /* pointer on a RX packet */
    int nb_pkt;

    /* local timestamp variables until we get accurate GPS time */
    struct timespec fetch_time;
    char fetch_timestamp[30];
    struct tm * x;

    arg = arg;
    /* main loop */
    while ((quit_sig != 1) && (exit_sig != 1)) {
        /* fetch packets */
        memset(rxpkt, 0, sizeof(rxpkt));
        pthread_mutex_lock(&pthrid_mtx);
        nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt);
        pthread_mutex_unlock(&pthrid_mtx);
        if (nb_pkt == LGW_HAL_ERROR) {
            MSG("ERROR: failed packet fetch, exiting\n");
            return;
        } else if (nb_pkt == 0) {
            /* wait a short time if no packets */
            wait_ms(FETCH_SLEEP_MS);
        } else {
            /* local timestamp generation until we get accurate GPS time */
            clock_gettime(CLOCK_REALTIME, &fetch_time);
            x = gmtime(&(fetch_time.tv_sec));
            sprintf(fetch_timestamp, "%04i-%02i-%02i %02i:%02i:%02i.%03liZ", (x->tm_year) + 1900, (x->tm_mon) + 1, x->tm_mday, x->tm_hour, x->tm_min, x->tm_sec, (fetch_time.tv_nsec) / 1000000); /* ISO 8601 format */
        }
        
        /* log packets */
        for (i = 0; i < nb_pkt; ++i) {
            p = &rxpkt[i];
            if(log_en)
                MSG("INFO: log into file \n");
            /* record received package into log file */
            record_log(log_file, p, fetch_timestamp);
            /* printf received package into stdout */
            MSG("INFO: Received package +++  ");
            record_log(stdout, p, fetch_timestamp);
        }

        /* check time and rotate log file if necessary */
        ++time_check;
        if (time_check >= 8) {
            time_check = 0;
            time(&now_time);
            if (difftime(now_time, log_start_time) > log_rotate_interval) {
                fclose(log_file);
                MSG("INFO: log file %s closed, %lu packet(s) recorded\n", log_file_name, pkt_in_log);
                pkt_in_log = 0;
                open_log();
            }
        }
    }
}


int parse_tx_parameters(const char * conf_file){
    const char conf_obj[] = "SX1301_conf";
    const char *str; /* used to store string value from JSON object */
    JSON_Value *root_val;
    JSON_Object *root = NULL;
    JSON_Object *conf = NULL;
    JSON_Value *val;

    /* try to parse JSON */
    root_val = json_parse_file_with_comments(conf_file);
    root = json_value_get_object(root_val);
    if (root == NULL) {
        MSG("ERROR: %s id not a valid JSON file\n", conf_file);
        exit(EXIT_FAILURE);
    }

    conf = json_object_get_object(root, conf_obj);
    if (conf == NULL) {
        MSG("WARNING: %s does not contain a JSON object named %s\n", conf_file, conf_obj);
        return -1;
    } else {
        MSG("INFO: %s does contain a JSON object named %s, parsing SX1301 tx parameters\n", conf_file, conf_obj);
    }

    val = json_object_get_value(conf, "tx_params"); /* fetch value (if possible) */
    if (json_value_get_type(val) != JSONObject) {
        MSG("INFO: no configuration for SX1301 tx paramters\n");
    } else {
        val = json_object_dotget_value(conf, "tx_params.mod");
        if (json_value_get_type(val) == JSONString) {
            str = json_value_get_string(val);
            if (!strncmp(str, "LORA", 4)) {
                strncpy(mod, "LORA", sizeof(mod));
                MSG("INFO: tx_params.mod = %s \n", mod);
            } else if (!strncmp(str, "FSK", 3)) {
                strncpy(mod, "FSK", sizeof(mod));
                MSG("INFO: tx_params.mod = %s \n", mod);
            } else {
                MSG("WARNING: invalid radio type: %s (should be LORA or FSK)\n", str);
            }
        } else {
            MSG("WARNING: Data type for mod seems wrong, please check\n");
            strncpy(mod, "LORA", sizeof(mod));
        }

        val = json_object_dotget_value(conf, "tx_params.rf_chain");
        if (json_value_get_type(val) == JSONNumber) {
            rf_chain = (int)json_value_get_number(val);
            rf_chain = (rf_chain > 1)?0:rf_chain;
            MSG("INFO: tx_params.rf_chain= %d \n", rf_chain);
        } else {
            MSG("WARNING: Data type for rf_chain seems wrong, please check\n");
            rf_chain = 0;
        }


        val = json_object_dotget_value(conf, "tx_params.freq");
        if (json_value_get_type(val) == JSONNumber) {
            if(cmd_f == 0){
                f_target = (uint32_t)json_value_get_number(val);
             }
            MSG("INFO: tx_params.freq = %d \n", f_target);
        } else {
            if(cmd_f == 0){
                f_target = 433100000;
                MSG("WARNING: Data type for frequency seems wrong, please check\n");
            }
            else{
                MSG("INFO: tx_params.freq = %d \n", f_target);
            }
        }

        val = json_object_dotget_value(conf, "tx_params.bw");
        if (json_value_get_type(val) == JSONNumber) {
            bw = (int)json_value_get_number(val);
            MSG("INFO: tx_params.bw = %d \n", bw);
        } else {
            MSG("WARNING: Data type for bw seems wrong, please check\n");
            bw = 125;
        }

        val = json_object_dotget_value(conf, "tx_params.sf");
        if (json_value_get_type(val) == JSONNumber) {
            sf = (int)json_value_get_number(val);
            MSG("INFO: tx_params.sf = %d \n", sf);
        } else {
            MSG("WARNING: Data type for sf seems wrong, please check\n");
            sf = 10;
        }

        val = json_object_dotget_value(conf, "tx_params.cr");
        if (json_value_get_type(val) == JSONNumber) {
            cr = (int)json_value_get_number(val);
            MSG("INFO: tx_params.cr = %d \n", cr);
        } else {
            MSG("WARNING: Data type for sf seems wrong, please check\n");
            cr = 1;
        }

        val = json_object_dotget_value(conf, "tx_params.power");
        if (json_value_get_type(val) == JSONNumber) {
            if(cmd_p == 0){
                power = (int)json_value_get_number(val);
            }
            MSG("INFO: tx_params.power = %d \n", power);
        } else {
            if(cmd_p == 0){
                MSG("WARNING: Data type for power seems wrong, please check\n");
                power = 14;
            }
            else{
                MSG("INFO: tx_params.power = %d \n", power);
            }
        }

        val = json_object_dotget_value(conf, "tx_params.pream_len");
        if (json_value_get_type(val) == JSONNumber) {
            preamb = (int)json_value_get_number(val);
            MSG("INFO: tx_params.pream_len = %d \n", preamb);
        } else {
            MSG("WARNING: Data type for preamble length seems wrong, please check\n");
            preamb = 8;
        }

        val = json_object_dotget_value(conf, "tx_params.payload_len");
        if (json_value_get_type(val) == JSONNumber) {
            pl_size = (int)json_value_get_number(val);
            MSG("INFO: tx_params.pi_size = %d \n", pl_size);
        } else {
            MSG("WARNING: Data type for sf seems wrong, please check\n");
            pl_size = 16;
        }

        val = json_object_dotget_value(conf, "tx_params.invert");
        if (json_value_get_type(val) == JSONBoolean) {
            invert = (bool)json_value_get_boolean(val);
            MSG("INFO: tx_params.invert = %d \n", invert);
        } else {
            MSG("WARNING: Data type for invert seems wrong, please check\n");
            invert = false;
        }

        val = json_object_dotget_value(conf, "tx_params.interval");
        if (json_value_get_type(val) == JSONNumber) {
            delay = (int)json_value_get_number(val);
            MSG("INFO: tx_params.interval = %d \n", delay);
        } else {
            MSG("WARNING: Data type for interval seems wrong, please check\n");
            delay = 1000;
        }

        val = json_object_dotget_value(conf, "tx_params.repeat");
        if (json_value_get_type(val) == JSONNumber) {
            repeat = (int)json_value_get_number(val);
            MSG("INFO: tx_params.repeat = %d \n", repeat);
        } else {
            MSG("WARNING: Data type for repeat seems wrong, please check\n");
            repeat = -1;
        }

    }

    json_value_free(root_val);
    return 0;
}


int parse_SX1301_configuration(const char * conf_file) {
    int i;
    const char conf_obj[] = "SX1301_conf";
    char param_name[32]; /* used to generate variable parameter names */
    const char *str; /* used to store string value from JSON object */
    struct lgw_conf_board_s boardconf;
    struct lgw_conf_rxrf_s rfconf;
    struct lgw_conf_rxif_s ifconf;
    JSON_Value *root_val;
    JSON_Object *root = NULL;
    JSON_Object *conf = NULL;
    JSON_Value *val;
    uint32_t sf, bw;

    /* try to parse JSON */
    root_val = json_parse_file_with_comments(conf_file);
    root = json_value_get_object(root_val);
    if (root == NULL) {
        MSG("ERROR: %s id not a valid JSON file\n", conf_file);
        exit(EXIT_FAILURE);
    }
    conf = json_object_get_object(root, conf_obj);
    if (conf == NULL) {
        MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj);
        return -1;
    } else {
        MSG("INFO: %s does contain a JSON object named %s, parsing SX1301 parameters\n", conf_file, conf_obj);
    }

    /* set board configuration */
    memset(&boardconf, 0, sizeof boardconf); /* initialize configuration structure */
    val = json_object_get_value(conf, "lorawan_public"); /* fetch value (if possible) */
    if (json_value_get_type(val) == JSONBoolean) {
        boardconf.lorawan_public = (bool)json_value_get_boolean(val);
    } else {
        MSG("WARNING: Data type for lorawan_public seems wrong, please check\n");
        boardconf.lorawan_public = false;
    }
    val = json_object_get_value(conf, "clksrc"); /* fetch value (if possible) */
    if (json_value_get_type(val) == JSONNumber) {
        boardconf.clksrc = (uint8_t)json_value_get_number(val);
    } else {
        MSG("WARNING: Data type for clksrc seems wrong, please check\n");
        boardconf.clksrc = 0;
    }
    MSG("INFO: lorawan_public %d, clksrc %d\n", boardconf.lorawan_public, boardconf.clksrc);
    /* all parameters parsed, submitting configuration to the HAL */
    if (lgw_board_setconf(boardconf) != LGW_HAL_SUCCESS) {
        MSG("ERROR: Failed to configure board\n");
        return -1;
    }
    
    /* set configuration for tx gains */
    memset(&txlut, 0, sizeof txlut); /* initialize configuration structure */
    for (i = 0; i < TX_GAIN_LUT_SIZE_MAX; i++) {
        snprintf(param_name, sizeof param_name, "tx_lut_%i", i); /* compose parameter path inside JSON structure */
        val = json_object_get_value(conf, param_name); /* fetch value (if possible) */
        if (json_value_get_type(val) != JSONObject) {
            MSG("INFO: no configuration for tx gain lut %i\n", i);
            continue;
        }
        txlut.size++; /* update TX LUT size based on JSON object found in configuration file */
        /* there is an object to configure that TX gain index, let's parse it */
        snprintf(param_name, sizeof param_name, "tx_lut_%i.pa_gain", i);
        val = json_object_dotget_value(conf, param_name);
        if (json_value_get_type(val) == JSONNumber) {
            txlut.lut[i].pa_gain = (uint8_t)json_value_get_number(val);
        } else {
            MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i);
            txlut.lut[i].pa_gain = 0;
        }
        snprintf(param_name, sizeof param_name, "tx_lut_%i.dac_gain", i);
        val = json_object_dotget_value(conf, param_name);
        if (json_value_get_type(val) == JSONNumber) {
            txlut.lut[i].dac_gain = (uint8_t)json_value_get_number(val);
        } else {
            txlut.lut[i].dac_gain = 3; /* This is the only dac_gain supported for now */
        }
        snprintf(param_name, sizeof param_name, "tx_lut_%i.dig_gain", i);
        val = json_object_dotget_value(conf, param_name);
        if (json_value_get_type(val) == JSONNumber) {
            txlut.lut[i].dig_gain = (uint8_t)json_value_get_number(val);
        } else {
            MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i);
            txlut.lut[i].dig_gain = 0;
        }
        snprintf(param_name, sizeof param_name, "tx_lut_%i.mix_gain", i);
        val = json_object_dotget_value(conf, param_name);
        if (json_value_get_type(val) == JSONNumber) {
            txlut.lut[i].mix_gain = (uint8_t)json_value_get_number(val);
        } else {
            MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i);
            txlut.lut[i].mix_gain = 0;
        }
        snprintf(param_name, sizeof param_name, "tx_lut_%i.rf_power", i);
        val = json_object_dotget_value(conf, param_name);
        if (json_value_get_type(val) == JSONNumber) {
            txlut.lut[i].rf_power = (int8_t)json_value_get_number(val);
        } else {
            MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i);
            txlut.lut[i].rf_power = 0;
        }
    }
    /* all parameters parsed, submitting configuration to the HAL */
    if (txlut.size > 0) {
        MSG("INFO: Configuring TX LUT with %u indexes\n", txlut.size);
        if (lgw_txgain_setconf(&txlut) != LGW_HAL_SUCCESS) {
            MSG("ERROR: Failed to configure concentrator TX Gain LUT\n");
            return -1;
        }
    } else {
        MSG("WARNING: No TX gain LUT defined\n");
    }




    /* set configuration for RF chains */
    for (i = 0; i < LGW_RF_CHAIN_NB; ++i) {
        memset(&rfconf, 0, sizeof(rfconf)); /* initialize configuration structure */
        sprintf(param_name, "radio_%i", i); /* compose parameter path inside JSON structure */
        val = json_object_get_value(conf, param_name); /* fetch value (if possible) */
        if (json_value_get_type(val) != JSONObject) {
            MSG("INFO: no configuration for radio %i\n", i);
            continue;
        }
        /* there is an object to configure that radio, let's parse it */
        sprintf(param_name, "radio_%i.enable", i);
        val = json_object_dotget_value(conf, param_name);
        if (json_value_get_type(val) == JSONBoolean) {
            rfconf.enable = (bool)json_value_get_boolean(val);
        } else {
            rfconf.enable = false;
        }
        if (rfconf.enable == false) { /* radio disabled, nothing else to parse */
            MSG("INFO: radio %i disabled\n", i);
        } else  { /* radio enabled, will parse the other parameters */
            snprintf(param_name, sizeof param_name, "radio_%i.freq", i);
            rfconf.freq_hz = (uint32_t)json_object_dotget_number(conf, param_name);
            snprintf(param_name, sizeof param_name, "radio_%i.rssi_offset", i);
            rfconf.rssi_offset = (float)json_object_dotget_number(conf, param_name);
            snprintf(param_name, sizeof param_name, "radio_%i.type", i);
            str = json_object_dotget_string(conf, param_name);
            if (!strncmp(str, "SX1255", 6)) {
                rfconf.type = LGW_RADIO_TYPE_SX1255;
            } else if (!strncmp(str, "SX1257", 6)) {
                rfconf.type = LGW_RADIO_TYPE_SX1257;
            } else {
                MSG("WARNING: invalid radio type: %s (should be SX1255 or SX1257)\n", str);
            }
            snprintf(param_name, sizeof param_name, "radio_%i.tx_enable", i);
            val = json_object_dotget_value(conf, param_name);
            if (json_value_get_type(val) == JSONBoolean) {
                rfconf.tx_enable = (bool)json_value_get_boolean(val);
            } else {
                rfconf.tx_enable = false;
            }
            MSG("INFO: radio %i enabled (type %s), center frequency %u, RSSI offset %f, tx enabled %d\n", i, str, rfconf.freq_hz, rfconf.rssi_offset, rfconf.tx_enable);
        }
        /* all parameters parsed, submitting configuration to the HAL */
        if (lgw_rxrf_setconf(i, rfconf) != LGW_HAL_SUCCESS) {
            MSG("ERROR: invalid configuration for radio %i\n", i);
            return -1;
        }
    }

    /* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */
    for (i = 0; i < LGW_MULTI_NB; ++i) {
        memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */
        sprintf(param_name, "chan_multiSF_%i", i); /* compose parameter path inside JSON structure */
        val = json_object_get_value(conf, param_name); /* fetch value (if possible) */
        if (json_value_get_type(val) != JSONObject) {
            MSG("INFO: no configuration for LoRa multi-SF channel %i\n", i);
            continue;
        }
        /* there is an object to configure that LoRa multi-SF channel, let's parse it */
        sprintf(param_name, "chan_multiSF_%i.enable", i);
        val = json_object_dotget_value(conf, param_name);
        if (json_value_get_type(val) == JSONBoolean) {
            ifconf.enable = (bool)json_value_get_boolean(val);
        } else {
            ifconf.enable = false;
        }
        if (ifconf.enable == false) { /* LoRa multi-SF channel disabled, nothing else to parse */
            MSG("INFO: LoRa multi-SF channel %i disabled\n", i);
        } else  { /* LoRa multi-SF channel enabled, will parse the other parameters */
            sprintf(param_name, "chan_multiSF_%i.radio", i);
            ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, param_name);
            sprintf(param_name, "chan_multiSF_%i.if", i);
            ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, param_name);
            // TODO: handle individual SF enabling and disabling (spread_factor)
            MSG("INFO: LoRa multi-SF channel %i enabled, radio %i selected, IF %i Hz, 125 kHz bandwidth, SF 7 to 12\n", i, ifconf.rf_chain, ifconf.freq_hz);
        }
        /* all parameters parsed, submitting configuration to the HAL */
        if (lgw_rxif_setconf(i, ifconf) != LGW_HAL_SUCCESS) {
            MSG("ERROR: invalid configuration for Lora multi-SF channel %i\n", i);
            return -1;
        }
    }

    /* set configuration for LoRa standard channel */
    memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */
    val = json_object_get_value(conf, "chan_Lora_std"); /* fetch value (if possible) */
    if (json_value_get_type(val) != JSONObject) {
        MSG("INFO: no configuration for LoRa standard channel\n");
    } else {
        val = json_object_dotget_value(conf, "chan_Lora_std.enable");
        if (json_value_get_type(val) == JSONBoolean) {
            ifconf.enable = (bool)json_value_get_boolean(val);
        } else {
            ifconf.enable = false;
        }
        if (ifconf.enable == false) {
            MSG("INFO: LoRa standard channel %i disabled\n", i);
        } else  {
            ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.radio");
            ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, "chan_Lora_std.if");
            bw = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.bandwidth");
            switch(bw) {
                case 500000:
                    ifconf.bandwidth = BW_500KHZ;
                    break;
                case 250000:
                    ifconf.bandwidth = BW_250KHZ;
                    break;
                case 125000:
                    ifconf.bandwidth = BW_125KHZ;
                    break;
                default:
                    ifconf.bandwidth = BW_UNDEFINED;
            }
            sf = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.spread_factor");
            switch(sf) {
                case  7:
                    ifconf.datarate = DR_LORA_SF7;
                    break;
                case  8:
                    ifconf.datarate = DR_LORA_SF8;
                    break;
                case  9:
                    ifconf.datarate = DR_LORA_SF9;
                    break;
                case 10:
                    ifconf.datarate = DR_LORA_SF10;
                    break;
                case 11:
                    ifconf.datarate = DR_LORA_SF11;
                    break;
                case 12:
                    ifconf.datarate = DR_LORA_SF12;
                    break;
                default:
                    ifconf.datarate = DR_UNDEFINED;
            }
            MSG("INFO: LoRa standard channel enabled, radio %i selected, IF %i Hz, %u Hz bandwidth, SF %u\n", ifconf.rf_chain, ifconf.freq_hz, bw, sf);
        }
        if (lgw_rxif_setconf(8, ifconf) != LGW_HAL_SUCCESS) {
            MSG("ERROR: invalid configuration for Lora standard channel\n");
            return -1;
        }
    }

    /* set configuration for FSK channel */
    memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */
    val = json_object_get_value(conf, "chan_FSK"); /* fetch value (if possible) */
    if (json_value_get_type(val) != JSONObject) {
        MSG("INFO: no configuration for FSK channel\n");
    } else {
        val = json_object_dotget_value(conf, "chan_FSK.enable");
        if (json_value_get_type(val) == JSONBoolean) {
            ifconf.enable = (bool)json_value_get_boolean(val);
        } else {
            ifconf.enable = false;
        }
        if (ifconf.enable == false) {
            MSG("INFO: FSK channel %i disabled\n", i);
        } else  {
            ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, "chan_FSK.radio");
            ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, "chan_FSK.if");
            bw = (uint32_t)json_object_dotget_number(conf, "chan_FSK.bandwidth");
            if      (bw <= 7800) {
                ifconf.bandwidth = BW_7K8HZ;
            } else if (bw <= 15600) {
                ifconf.bandwidth = BW_15K6HZ;
            } else if (bw <= 31200) {
                ifconf.bandwidth = BW_31K2HZ;
            } else if (bw <= 62500) {
                ifconf.bandwidth = BW_62K5HZ;
            } else if (bw <= 125000) {
                ifconf.bandwidth = BW_125KHZ;
            } else if (bw <= 250000) {
                ifconf.bandwidth = BW_250KHZ;
            } else if (bw <= 500000) {
                ifconf.bandwidth = BW_500KHZ;
            } else {
                ifconf.bandwidth = BW_UNDEFINED;
            }
            ifconf.datarate = (uint32_t)json_object_dotget_number(conf, "chan_FSK.datarate");
            MSG("INFO: FSK channel enabled, radio %i selected, IF %i Hz, %u Hz bandwidth, %u bps datarate\n", ifconf.rf_chain, ifconf.freq_hz, bw, ifconf.datarate);
        }
        if (lgw_rxif_setconf(9, ifconf) != LGW_HAL_SUCCESS) {
            MSG("ERROR: invalid configuration for FSK channel\n");
            return -1;
        }
    }
    json_value_free(root_val);
    return 0;
}

int parse_gateway_configuration(const char * conf_file) {
    const char conf_obj[] = "gateway_conf";
    JSON_Value *root_val;
    JSON_Object *root = NULL;
    JSON_Object *conf = NULL;
    const char *str; /* pointer to sub-strings in the JSON data */
    unsigned long long ull = 0;

    /* try to parse JSON */
    root_val = json_parse_file_with_comments(conf_file);
    root = json_value_get_object(root_val);
    if (root == NULL) {
        MSG("ERROR: %s id not a valid JSON file\n", conf_file);
        exit(EXIT_FAILURE);
    }
    conf = json_object_get_object(root, conf_obj);
    if (conf == NULL) {
        MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj);
        return -1;
    } else {
        MSG("INFO: %s does contain a JSON object named %s, parsing gateway parameters\n", conf_file, conf_obj);
    }

    /* getting network parameters (only those necessary for the packet logger) */
    str = json_object_get_string(conf, "gateway_ID");
    if (str != NULL) {
        sscanf(str, "%llx", &ull);
        lgwm = ull;
        MSG("INFO: gateway MAC address is configured to %016llX\n", ull);
    }

    json_value_free(root_val);
    return 0;
}

void open_log(void) {
    int i;
    char iso_date[20];

    strftime(iso_date, ARRAY_SIZE(iso_date), "%Y%m%dT%H%M%SZ", gmtime(&now_time)); /* format yyyymmddThhmmssZ */
    log_start_time = now_time; /* keep track of when the log was started, for log rotation */

    if(log_en == false){
        log_file = fopen("/dev/NULL", "a"); /* create log file, append if file already exist */
        return;
    }
    
    if(-1 == access(LOG_PATH_DEFAULT, F_OK)){
        if(-1 == mkdir(LOG_PATH_DEFAULT, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)){
            perror("mkdir");
        }
        else{
            sprintf(log_path_name, "%s/", LOG_PATH_DEFAULT);
        }
    }
    else{
        sprintf(log_path_name, "%s/", LOG_PATH_DEFAULT);
    }

    sprintf(log_file_name, "pktlog_%s_%s.csv", lgwm_str, iso_date);
    strcat(log_path_name, log_file_name);
    sprintf(log_file_name, "%s", log_path_name);

    
    log_file = fopen(log_file_name, "a"); /* create log file, append if file already exist */
    if (log_file == NULL) {
        MSG("ERROR: impossible to create log file %s\n", log_file_name);
        exit(EXIT_FAILURE);
    }

    i = fprintf(log_file, "\"gateway ID\",\"node MAC\",\"UTC timestamp\",\"us count\",\"frequency\",\"RF chain\",\"RX chain\",\"status\",\"size\",\"modulation\",\"bandwidth\",\"datarate\",\"coderate\",\"RSSI\",\"SNR\",\"payload\"\n");
    if (i < 0) {
        MSG("ERROR: impossible to write to log file %s\n", log_file_name);
        exit(EXIT_FAILURE);
    }

    MSG("INFO: Now writing to log file %s\n", log_file_name);
    return;
}

/* open log */
void record_log(FILE *log, struct lgw_pkt_rx_s *p, char *timestamp)
{
    int j;
    //if(p->status != STAT_CRC_OK) 
        //return;

    /* writing gateway ID */
    fprintf(log, "\"%08X%08X\",", (uint32_t)(lgwm >> 32), (uint32_t)(lgwm & 0xFFFFFFFF));
    
    /* writing node MAC address */
    fputs("\"\",", log); // TODO: need to parse payload
    
    /* writing UTC timestamp*/
    fprintf(log, "\"%s\",", timestamp);
    // TODO: replace with GPS time when available
    
    /* writing internal clock */
    fprintf(log, "%10u,", p->count_us);
    
    /* writing RX frequency */
    fprintf(log, "%10u,", p->freq_hz);
    
    /* writing RF chain */
    fprintf(log, "%u,", p->rf_chain);
    
    /* writing RX modem/IF chain */
    fprintf(log, "%2d,", p->if_chain);
    
    /* writing status */
    switch(p->status) {
        case STAT_CRC_OK:
            fputs("\"CRC_OK\" ,", log);
            break;
        case STAT_CRC_BAD:
            fputs("\"CRC_BAD\",", log);
            break;
        case STAT_NO_CRC:
            fputs("\"NO_CRC\" ,", log);
            break;
        case STAT_UNDEFINED:
            fputs("\"UNDEF\"  ,", log);
            break;
        default:
            fputs("\"ERR\"    ,", log);
    }
    
    /* writing payload size */
    fprintf(log, "%3u,", p->size);
    
    /* writing modulation */
    switch(p->modulation) {
        case MOD_LORA:
            fputs("\"LORA\",", log);
            break;
        case MOD_FSK:
            fputs("\"FSK\" ,", log);
            break;
        default:
            fputs("\"ERR\" ,", log);
    }
    
    /* writing bandwidth */
    switch(p->bandwidth) {
        case BW_500KHZ:
            fputs("500000,", log);
            break;
        case BW_250KHZ:
            fputs("250000,", log);
            break;
        case BW_125KHZ:
            fputs("125000,", log);
            break;
        case BW_62K5HZ:
            fputs("62500 ,", log);
            break;
        case BW_31K2HZ:
            fputs("31200 ,", log);
            break;
        case BW_15K6HZ:
            fputs("15600 ,", log);
            break;
        case BW_7K8HZ:
            fputs("7800  ,", log);
            break;
        case BW_UNDEFINED:
            fputs("0     ,", log);
            break;
        default:
            fputs("-1    ,", log);
    }
    
    /* writing datarate */
    if (p->modulation == MOD_LORA) {
        switch (p->datarate) {
            case DR_LORA_SF7:
                fputs("\"SF7\"   ,", log);
                break;
            case DR_LORA_SF8:
                fputs("\"SF8\"   ,", log);
                break;
            case DR_LORA_SF9:
                fputs("\"SF9\"   ,", log);
                break;
            case DR_LORA_SF10:
                fputs("\"SF10\"  ,", log);
                break;
            case DR_LORA_SF11:
                fputs("\"SF11\"  ,", log);
                break;
            case DR_LORA_SF12:
                fputs("\"SF12\"  ,", log);
                break;
            default:
                fputs("\"ERR\"   ,", log);
        }
    } else if (p->modulation == MOD_FSK) {
        fprintf(log, "\"%6u\",", p->datarate);
    } else {
        fputs("\"ERR\"   ,", log);
    }
    
    /* writing coderate */
    switch (p->coderate) {
        case CR_LORA_4_5:
            fputs("\"4/5\",", log);
            break;
        case CR_LORA_4_6:
            fputs("\"2/3\",", log);
            break;
        case CR_LORA_4_7:
            fputs("\"4/7\",", log);
            break;
        case CR_LORA_4_8:
            fputs("\"1/2\",", log);
            break;
        case CR_UNDEFINED:
            fputs("\"\"   ,", log);
            break;
        default:
            fputs("\"ERR\",", log);
    }
    
    /* writing packet RSSI */
    fprintf(log, "%+.0f,", p->rssi);
    
    /* writing packet average SNR */
    fprintf(log, "%+5.1f,", p->snr);
    
    /* writing hex-encoded payload (bundled in 32-bit words) */
    fputs("\"", log);
    if(p->size >= 256){
        fputs("----payload size error---", log);
    }
    else{
        for (j = 0; j < p->size; ++j) {
            if ((j > 0) && (j % 4 == 0)) {
                fputs("-", log);
            }
            fprintf(log, "%02X", p->payload[j]);
        }
    }
    /* end of log file line */
    fputs("\"\n", log);
    fflush(log);

    if((log != stdout) && (log != stderr)){
        ++pkt_in_log;
    }
    
}



/* describe command line options */
void usage(void) {
    printf("*** Library version information ***\n%s\n\n", lgw_version_info());
    printf("Available options:\n");
    printf(" -h print this help\n");
    printf(" -c <int>  config ISM band 433 ,435 or 470 \n");
    printf("            => default ISM band 433 \n");
    printf(" -d <path> COM device to be used to access the concentrator board\n");
    printf("            => default path: " COM_PATH_DEFAULT "\n");
    printf(" -r <int> rotate log file every N seconds (-1 disable log rotation)\n");
    printf("            => default: 3600 seconds \n");
    printf(" -m <int> test mode for tx, rx, or both  (0:both, 1:tx, 2:rx, 3:tx_continus, 4:tx_continus and rx)\n");
    printf("            => default: both \n");
    printf(" -f <float> config tx freqency unit MHz (example : 433.1, 433.5, 435.1)\n");
    printf("            => tx freq range from 433.1 to 472.9 MHz \n");
    printf(" -p <int>  RF power (dBm) [-6,-3,0,3,6,10,11,12,13,14,16,20,23,25,26,27] \n");
    printf(" -l enable record log into file (default:disable)\n");
}


void mcu_reset_control(void)
{
    int i = -1;

    i = system("echo 1 >> /sys/class/gpio/gpio60/value");
    if(i < 0){
        MSG("WARNING: set gpio60 failed \n");
        return;
    }
    MSG("INFO: echo 1 >> /sys/class/gpio/gpio60/value \n");
    wait_ms(100);
}

/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */

int main(int argc, char **argv) {
    int i; /* loop and temporary variables */
    double xf = 0.0;
    int xp = 0;
    int band = 0; /* ISM BAND 433/435/470 */
    /* configuration file related */
    const char *global_conf_fname;
    const char global_conf_default_fname[] = "global_conf.json"; /* contain global (typ. network-wide) configuration */
    const char local_conf_fname[] = "local_conf.json"; /* contain node specific configuration, overwrite global parameters for parameters that are defined in both */
    const char debug_conf_fname[] = "debug_conf.json"; /* if present, all other configuration files are ignored */

    const char global_conf433_fname[] = "global_conf_433.json";
    const char global_conf435_fname[] = "global_conf_435.json";
    const char global_conf470_fname[] = "global_conf_470.json";

    /* COM interfaces */
    const char com_path_default[] = COM_PATH_DEFAULT;
    const char *com_path = com_path_default;

    /* set gpio60 high */
    mcu_reset_control();
    
    /* parse command line options */
    while ((i = getopt (argc, argv, "hr:c:d:m:f:p:l")) != -1) {
        switch (i) {
            case 'h':
                usage();
                return EXIT_FAILURE;
                break;
            case 'c':
                i = sscanf(optarg, "%i", &band);
                if((i != 1) || ((band != 433) && (band != 435) && (band != 470))){
                    MSG( "ERROR: Invalid argument for -c option\n");
                    MSG( "         ==> parameter must be 433 or 435 or 470 \n");
                    return EXIT_FAILURE;
                }
                break;
                
            case 'd':
                if (optarg != NULL) {
                    com_path = optarg;
                }
                break;

            case 'r':
                log_rotate_interval = atoi(optarg);
                if ((log_rotate_interval == 0) || (log_rotate_interval < -1)) {
                    MSG( "ERROR: Invalid argument for -r option\n");
                    return EXIT_FAILURE;
                }
                break;

            case 'm':
                module_type = (uint8_t)atoi(optarg);
                if(module_type > 4){
                    MSG( "ERROR: Invalid argument for -m option\n");
                    return EXIT_FAILURE;
                }
                break;
                
            case 'f':
                i = sscanf(optarg, "%lf", &xf);
                if ((i != 1) || (xf < 433.1) || (xf > 472.9)) {
                     MSG("ERROR: invalid TX frequency\n");
                     usage();
                     return EXIT_FAILURE;
                 } else {
                     f_target = (uint32_t)((xf * 1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */
                     cmd_f = 1;
                 }
                break;

            case 'p':
                i = sscanf(optarg, "%i", &xp);
                if ((i != 1) || (xp < -60) || (xp > 60)) {
                    MSG("ERROR: invalid RF power\n");
                    usage();
                    return EXIT_FAILURE;
                } else {
                    power = xp;
                    cmd_p = 1;
                }
                break;

            case 'l':
                log_en = true;
                break;

            default:
                MSG("ERROR: argument parsing use -h option for help\n");
                usage();
                return EXIT_FAILURE;
        }
    }

    /* register function to be called for exit cleanups */
    atexit(exit_cleanup);

    /* configure signal handling */
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigact.sa_handler = sig_handler;
    sigaction(SIGQUIT, &sigact, NULL);
    sigaction(SIGINT, &sigact, NULL);
    sigaction(SIGTERM, &sigact, NULL);

    MSG("INFO: lgw_connecting...\n");
    /* Open communication bridge */
    i = lgw_connect(com_path);
    if (i == -1) {
        printf("ERROR: FAIL TO CONNECT BOARD ON %s\n", com_path);
        exit(EXIT_FAILURE);
    }
    MSG("INFO: lgw_connected.\n");
    MSG("INFO: parse json file and configure sx1301.\n");
    if(band == 0){
        global_conf_fname = global_conf_default_fname;
    }
    else if(band == 433){
        global_conf_fname = global_conf433_fname;
    }
    else if(band == 435){
        global_conf_fname = global_conf435_fname;
    }
    else if(band == 470){
        global_conf_fname = global_conf470_fname;
    }
    else{
        global_conf_fname = global_conf_default_fname;
    }

    /* configuration files management */
    if (access(debug_conf_fname, R_OK) == 0) {
        /* if there is a debug conf, parse only the debug conf */
        MSG("INFO: found debug configuration file %s, other configuration files will be ignored\n", debug_conf_fname);
        parse_SX1301_configuration(debug_conf_fname);
        parse_tx_parameters(debug_conf_fname);
        parse_gateway_configuration(debug_conf_fname);
    } else if (access(global_conf_fname, R_OK) == 0) {
        /* if there is a global conf, parse it and then try to parse local conf  */
        MSG("INFO: found global configuration file %s, trying to parse it\n", global_conf_fname);
        parse_SX1301_configuration(global_conf_fname);
        parse_tx_parameters(global_conf_fname);
        parse_gateway_configuration(global_conf_fname);
        if (access(local_conf_fname, R_OK) == 0) {
            MSG("INFO: found local configuration file %s, trying to parse it\n", local_conf_fname);
            parse_SX1301_configuration(local_conf_fname);
            parse_tx_parameters(local_conf_fname);
            parse_gateway_configuration(local_conf_fname);
        }
    } else if (access(local_conf_fname, R_OK) == 0) {
        /* if there is only a local conf, parse it and that's all */
        MSG("INFO: found local configuration file %s, trying to parse it\n", local_conf_fname);
        parse_SX1301_configuration(local_conf_fname);
        parse_tx_parameters(local_conf_fname);
        parse_gateway_configuration(local_conf_fname);
    } else {
        MSG("ERROR: failed to find any configuration file named %s, %s or %s\n", global_conf_fname, local_conf_fname, debug_conf_fname);
        exit(EXIT_FAILURE);
    }


    /* TX gain config */
    //MSG("INFO: TX gain config.\n");
    //lgw_txgain_setconf(&txgain_lut);
    
    /* starting the concentrator */
    MSG("INFO: lgw_start...\n");
    i = lgw_start();
    if (i == LGW_HAL_SUCCESS) {
        MSG("INFO: concentrator started, packet can now be received\n");
    } else {
        MSG("ERROR: failed to start the concentrator\n");
        exit(EXIT_FAILURE);
    }

    
    /* transform the MAC address into a string */
    sprintf(lgwm_str, "%08X%08X", (uint32_t)(lgwm >> 32), (uint32_t)(lgwm & 0xFFFFFFFF));
    MSG("*** INFO: Module test %s ***\n", m_str[module_type]);
    /* opening log file and writing CSV header*/
    time(&now_time);
    open_log();

    switch(module_type){
        case 0:
            if(log_en == false)
                MSG("*** INFO: will not record received package into log file ***\n");
            else
                MSG("*** INFO: will record received package into log file ***\n");
            /* create tx test thread */
            pthread_create(&pthread_tx, NULL, (void*)tx_thread, NULL);
            /* create rx test thread */
            pthread_create(&pthread_rx, NULL, (void *)rx_thread, NULL);
            break;
        case 1:
            /* create tx test thread */
            pthread_create(&pthread_tx, NULL, (void*)tx_thread, NULL);
            break;
        case 2:
            if(log_en == false)
                MSG("*** INFO: will not record received package into log file ***\n");
            else
                MSG("*** INFO: will record received package into log file ***\n");
            /* create rx test thread */
            pthread_create(&pthread_rx, NULL, (void *)rx_thread, NULL);
            break;
        case 3:
            pthread_create(&pthread_tx, NULL, (void*)tx_thread, (void*)&module_type);
            break;
        case 4:
            pthread_create(&pthread_tx, NULL, (void*)tx_thread, (void*)&module_type);
            /* create rxx test thread */
            pthread_create(&pthread_rx, NULL, (void *)rx_thread, NULL);
        default:
            break;
    }

    switch(module_type){
        case 0:
        case 4:
            pthread_join(pthread_tx, NULL);
            pthread_join(pthread_rx, NULL);
            break;
        case 1:
        case 3:
            pthread_join(pthread_tx, NULL);
            break;
        case 2:
            pthread_join(pthread_rx, NULL);
            break;
        default:
            break;
    }

    /* exit loop on user signals */

    MSG("INFO: Exiting module test program\n");
    fclose(log_file);
    MSG("INFO: log file %s closed, %lu packet(s) recorded\n", log_file_name, pkt_in_log);

    exit(EXIT_SUCCESS);
}

/* --- EOF ------------------------------------------------------------------ */
